package com.demo.support.util.File;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

public class UploadBytes {

	// 文件名的标志位
	public static final String SIGN_FILENAME = "filename=\"";
	// 表单域名称的标志位
	public static final String FORM_NAME = "name=\"";
	// 以引号结束的标志位（文件名和表单域名称后的内容）
	public static final String QUOTE_FLAG = "\"";
	// 含文件名的表单域的上传文件类型 Content-type
	private final static String CONTENT_TYPE = "Content-Type:";
	// 自动换行符(上传文件类型名称后面的内容 实际上传文件内容之前的换行符)
	private final static String AUTO_LINE = "\r\n\r\n";
	private static final String SIGN_MULTIDATA = "multipart/form-data";
	// 默认编码
	private final static String DEFAULT_ENCODING = "UTF-8";

	/**
	 * buff为当前已经读取的上传流字节 boundary为HTTP头信息中的Content-Type指定的当前域的分隔符的字节数组
	 * 处理思想即找到一组成对的boundary，那么其中的字节即为表单域的数据.
	 * 
	 * @param boundary
	 *            分隔符
	 * @param buff
	 *            输出流
	 * @return 各分隔符之间的字节列表
	 * @throws Exception
	 */
	public static List<byte[]> findFiled(byte[] boundary, ByteArrayOutputStream buff) throws Exception {

		// 当前的数据连成对的boundary长度都不够，直接返回。
		if (buff.size() < boundary.length * 2) {
			return Collections.emptyList();
		} else {
			byte[] data = buff.toByteArray();// 当前数据

			List<byte[]> fileds = new ArrayList<byte[]>();
			int point = 0;// 当前指针位置
			int alertStart = 0;// 最近一次警戒区的位置
			boolean alert = false;// 是否处于警戒区
			int treated = 0;// 已经处理的字节长度

			while (true) {
				if (!alert && data.length - point < boundary.length * 2) {
					break;
				} else if (alert && data.length - point < boundary.length) {
					break;
				}

				if (isBytes(data, boundary, point)) {
					if (!alert) { // start line
						alert = true;
						alertStart = point + boundary.length;
						point = alertStart;
					} else { // end line
						byte[] filed;
						filed = Arrays.copyOfRange(data, alertStart, point);
						treated += (point - alertStart + boundary.length);

						fileds.add(filed);
						alert = false;
					}
				} else {
					point++;
				}
			}

			if (fileds.size() > 0) {
				buff.reset();
				buff.write(data, treated, data.length - treated);
				return fileds;
			}

			return Collections.emptyList();
		}

	}

	/**
	 * 根据解析上传文件的所有分隔符之间的字节数组列表 来获取各个文件上传的文件名所在表单域的字节数组列表 即含“filename=”的表单域字节数组
	 * 
	 * @param filesByte
	 *            所有分隔符之间的字节数组列表
	 * @param charset
	 *            上传文件的字符集
	 * @return 各个文件上传的文件名所在表单域的字节数组列表
	 */
	public static List<byte[]> getFilesByteList(List<byte[]> filesByte, String charset) throws UnsupportedEncodingException {
		List<byte[]> fieldFilesByte = new ArrayList<byte[]>();

		byte[] fileName = SIGN_FILENAME.getBytes(charset);
		for (int i = 0; i < filesByte.size(); i++) {
			byte[] bs = filesByte.get(i);
			int point = 0;
			while (true) {
				if (bs.length - point < fileName.length) {
					break;
				}
				if (isBytes(bs, fileName, point)) {
					fieldFilesByte.add(bs);
					break;
				} else {
					point++;
				}
			}
		}

		return fieldFilesByte;
	}

	/**
	 * 根据获取上传文件实际域的数据的字节 获取实际域数据
	 * 
	 * @param fieldByte
	 *            实际域的数据的字节
	 * @param charset
	 *            上传文件的编码
	 * @return 实际域的数据
	 */
	public static Field getField(byte[] fieldByte, String charset) throws UnsupportedEncodingException, IOException {
		Field field = new Field();

		String fileNameStr = getFieldStr(fieldByte, charset, SIGN_FILENAME, QUOTE_FLAG);

		field.setFieldName(fileNameStr);
		String fileName = "";
		if (fileNameStr != null && !fileNameStr.isEmpty()) {
			fileName = getExtensionName(fileNameStr);
		}
		field.setFileName(fileName);

		String fileType = getFieldStr(fieldByte, charset, CONTENT_TYPE, AUTO_LINE);
		field.setContentType(fileType);

		byte[] realFileBytes = getRealFileByte(fieldByte, charset);
		field.setFieldData(realFileBytes);

		return field;
	}

	/**
	 * 根据域数据获取域中标志的名称
	 * 
	 * @param fieldByte
	 *            域数据
	 * @param charset
	 *            编码
	 * @param nameFlag
	 *            名称的标志
	 * @param endFlag
	 *            以该标志位结束的内容
	 * @return 名称
	 */
	private static String getFieldStr(byte[] fieldByte, String charset, String nameFlag, String endFlag) throws UnsupportedEncodingException {

		byte[] fileNameByte = nameFlag.getBytes(charset);
		int point = 0;
		while (true) {
			if (fieldByte.length - point < fileNameByte.length) {
				break;
			}
			if (isBytes(fieldByte, fileNameByte, point)) {
				break;
			} else {
				point++;
			}
		}

		int pos = point + fileNameByte.length;

		// 截取从名称开始到内容结束的部分的字节数组
		byte[] fileStart = Arrays.copyOfRange(fieldByte, pos, fieldByte.length);

		// 从文件名开始 第一个引号 " 出现的位置 （即为文件名后面的第一个引号）
		byte[] quoteBytes = endFlag.getBytes(charset);
		int point1 = 0;
		while (true) {
			if (fileStart.length - point1 < quoteBytes.length) {
				break;
			}
			if (isBytes(fileStart, quoteBytes, point1)) {
				break;
			} else {
				point1++;
			}
		}

		// 名称开始到名称结束的字节数组
		byte[] nameBytes = Arrays.copyOfRange(fieldByte, pos, pos + point1);
		if (nameBytes == null || nameBytes.length == 0) {
			return null;
		}
		String result = new String(nameBytes, charset);
		// 若结果前后含有空格，则去掉前后的空格
		result = result.trim();
		return result;
	}

	/**
	 * 根据带文件名的文件表单域字节获取实际要上传的字节内容
	 * 
	 * @param fieldByte
	 *            带文件名的文件表单域字节
	 * @param charset
	 *            文件的字符集
	 * @return 实际要上传的字节内容
	 */
	public static byte[] getRealFileByte(byte[] fieldByte, String charset) throws UnsupportedEncodingException, FileNotFoundException, IOException {

		// 自动换行开始的位置
		byte[] autoLineByte = AUTO_LINE.getBytes(charset);
		int point = 0;
		while (true) {
			if (fieldByte.length - point < autoLineByte.length) {
				break;
			}
			if (isBytes(fieldByte, autoLineByte, point)) {
				break;
			} else {
				point++;
			}
		}

		// 上传文件的实际内容（自动换行结束的位置开始，内容的结束有一个换行符）
		byte[] realFileByte = Arrays.copyOfRange(fieldByte, point + autoLineByte.length, fieldByte.length - 2);

		return realFileByte;
	}

	/**
	 * 比较源数据中是否有与给定的字节数组匹配的数据
	 * 
	 * @param data
	 *            源数据
	 * @param byteGiven
	 *            要比较的数据
	 * @param point
	 *            当前指针所在的位置
	 * @return
	 */
	public static boolean isBytes(byte[] data, byte[] byteGiven, int point) {
		for (int i = 0; i < byteGiven.length; i++) {
			if (data[point + i] != byteGiven[i]) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 获取上传文件的字符集
	 * 
	 * @param request
	 *            请求
	 * @return 如果设置字符集的话 则返回字符集，否则 返回默认的字符集
	 */
	public static String getCharset(HttpServletRequest request) {
		String contentType = request.getContentType();
		//System.err.println(contentType);
		String charset = null;
		if (charset == null || charset.isEmpty()) {
			charset = DEFAULT_ENCODING;
		}
		return charset;
	}

	/**
	 * 获取请求的信息
	 * 
	 * @param request
	 *            请求
	 * @return 请求信息byte数组
	 * @throws IOException
	 */
	public static byte[] getContentBytes(HttpServletRequest request) throws IOException {
		InputStream sis = request.getInputStream();
		int dataLength = request.getContentLength();
		// 保存上传图片的数据
		byte[] dataBytes = new byte[dataLength];
		int byteRead = 0;
		int totalBytes = 0;
		// 上传的数据保存在byte数组
		while (totalBytes < dataLength) {
			byteRead = sis.read(dataBytes, totalBytes, dataLength);
			totalBytes += byteRead;
		}
		sis.close();
		return dataBytes;
	}

	/**
	 * 取得数据的分隔字符串
	 * 
	 * @param request
	 *            请求
	 * @return 数据的分隔字符串
	 */
	public static String getBoundaryStr(HttpServletRequest request) {
		String contentType = request.getContentType();
		int lastIndex = contentType.lastIndexOf("=");
		// 取得数据的分隔字符串
		String boundaryStr = "--" + contentType.substring(lastIndex + 1, contentType.length());
		return boundaryStr;
	}

	/**
	 * 获取上传文件的扩展名
	 * 
	 * @param fileName
	 *            文件名
	 * @return 文件名的扩展名
	 */
	public static String getExtensionName(String fileName) {
		int format = fileName.lastIndexOf(".");
		String extensionName = "";
		if (format >= 0) {
			extensionName = fileName.substring(format);
		}
		return extensionName;
	}

	/**
	 * 判断上传文件类型，如果是multipart/form-data，则返回true，否则返回fase
	 * 
	 * @param request
	 *            请求
	 * @return boolean
	 */
	public static boolean getContentTypeFlag(HttpServletRequest request) {
		String contentType = request.getContentType();
		if (contentType != null && !contentType.isEmpty()) {
			if (contentType.indexOf(SIGN_MULTIDATA) >= 0) {
				return true;
			}
		}
		return false;
	}
}